/* ***** BEGIN LICENSE BLOCK *****
 * Copyright (C) 2010-2011, The VNREAL Project Team.
 * 
 * This work has been funded by the European FP7
 * Network of Excellence "Euro-NF" (grant agreement no. 216366)
 * through the Specific Joint Developments and Experiments Project
 * "Virtual Network Resource Embedding Algorithms" (VNREAL). 
 *
 * The VNREAL Project Team consists of members from:
 * - University of Wuerzburg, Germany
 * - Universitat Politecnica de Catalunya, Spain
 * - University of Passau, Germany
 * See the file AUTHORS for details and contact information.
 * 
 * This file is part of ALEVIN (ALgorithms for Embedding VIrtual Networks).
 *
 * ALEVIN is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License Version 3 or later
 * (the "GPL"), or the GNU Lesser General Public License Version 3 or later
 * (the "LGPL") as published by the Free Software Foundation.
 *
 * ALEVIN is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
 * or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * or the GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License and
 * GNU Lesser General Public License along with ALEVIN; see the file
 * COPYING. If not, see <http://www.gnu.org/licenses/>.
 *
 * ***** END LICENSE BLOCK ***** */
package vnreal.algorithms.utils;

/**
 * Class to generate the data file needed by the LP solver 
 * to solve the LP problem. This data file will be processed 
 * with the models HHVNE-Model.mod and VNE-Model-NodeMapping.mod 
 * in /ILP-LP-Models solving a multicommodity flow problem with 
 * the substrate and virtual networks.
 * 
 *  See:
 *  M. Pioro and D. Medhi, Routing, Flow and Capacity Design in
 *  Communication and Computer Networks. San Francisco, CA: Morgan
 *  Kaufmann, 2004.
 * 
 * The data file and model file follow the GNU mathProg language. See:
 * 
 * http://www.gnu.org/software/glpk/#TOCdocumentation
 * 
 * @author Juan Felipe Botero
 * @since 2010-11-20
 */

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import vnreal.demands.AbstractDemand;
import vnreal.demands.BandwidthDemand;
import vnreal.demands.CpuDemand;
import vnreal.network.substrate.SubstrateLink;
import vnreal.network.substrate.SubstrateNetwork;
import vnreal.network.substrate.SubstrateNode;
import vnreal.network.virtual.VirtualLink;
import vnreal.network.virtual.VirtualNetwork;
import vnreal.network.virtual.VirtualNode;
import vnreal.resources.AbstractResource;
import vnreal.resources.BandwidthResource;
import vnreal.resources.CpuResource;

public class dataSolverFile {
	public dataSolverFile(String aFileName) {
		try {
			this.fstream = new FileWriter(aFileName);
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	/**
	 * Function to create the input file for the LP-solver (GLPK)
	 * 
	 * @param substrate
	 *            Substrate Network
	 * @param subsOriginal
	 *            Original substrate network before augmentation
	 * @param vNet
	 *            VNR to be mapped
	 * @param nodeMapping
	 *            Indication of the performed node mapping
	 * @param wBandwidth
	 *            Bandwidth considered weight
	 * @param wCPU
	 *            Cpu considered weight
	 * @param isNodeMapping
	 *            boolean to know if the data file will be for node or link
	 *            mapping
	 */

	public void createDataSolverFile(SubstrateNetwork substrate,
			SubstrateNetwork subsOriginal, VirtualNetwork vNet,
			Map<VirtualNode, SubstrateNode> nodeMapping, double wBandwidth,
			double wCPU, boolean isNodeMapping) {
		boolean flag = true;
		SubstrateNode tempMapped, tempSrc, tempDst;
		VirtualNode tempVirNode;
		VirtualLink currVLink;
		Map<String, Double> linksToMap = new LinkedHashMap<String, Double>();
		Map<String, Double> linksToMapHhCpu = new LinkedHashMap<String, Double>();
		BandwidthDemand tempBwDem = null;
		CpuDemand tempHhCpuDem = null;

		try {
			// Create file
			BufferedWriter out = new BufferedWriter(this.fstream);
			out.write("/* Data file autogenerated */");
			out.write("\n\ndata;\n\n");
			out.write("set SNnodes :=");
			// Write substrate nodes in data file
			for (SubstrateNode n : substrate.getVertices())
				out.write(" " + Integer.toString(n.getId()));

			if (isNodeMapping) {
				// For coordinate node and link mapping algorithm
				// The original substrate in node mapping
				out.write(";\n\n");
				out.write("set SNnodesOriginal :=");
				// Write substrate nodes in data file
				for (SubstrateNode n : subsOriginal.getVertices())
					out.write(" " + Integer.toString(n.getId()));
			}

			out.write(";\n\n");
			out.write("set SNlinks :=");
			// Write substrate links in data file
			for (Iterator<SubstrateLink> links = substrate.getEdges()
					.iterator(); links.hasNext();) {
				SubstrateLink temp = links.next();
				out.write(" ("
						+ Integer.toString(substrate.getSource(temp).getId())
						+ ","
						+ Integer.toString(substrate.getDest(temp).getId())
						+ ")");
			}

			if (isNodeMapping) {
				out.write(";\n\n");
				out.write("set SNlinksOriginal :=");
				// Write substrate links in data file
				for (Iterator<SubstrateLink> links = subsOriginal.getEdges()
						.iterator(); links.hasNext();) {
					SubstrateLink temp = links.next();
					out.write(" ("
							+ Integer.toString(substrate.getSource(temp)
									.getId()) + ","
							+ Integer.toString(substrate.getDest(temp).getId())
							+ ")");
				}
			}

			out.write(";\n\n");
			out.write("set VNnodes :=");
			List<SubstrateNode> mappedNodes = new LinkedList<SubstrateNode>();
			// Write virtual nodes in data file
			for (Iterator<VirtualNode> itt = vNet.getVertices().iterator(); itt
					.hasNext();) {
				tempVirNode = itt.next();
				tempMapped = nodeMapping.get(tempVirNode);

				if (!mappedNodes.contains(tempMapped))
					out.write(" " + Integer.toString(tempMapped.getId()));

				mappedNodes.add(tempMapped);
			}

			out.write(";\n\n");
			out.write("set VNlinks :=");
			// Write virtual links in data file
			for (Iterator<VirtualLink> links = vNet.getEdges().iterator(); links
					.hasNext();) {
				currVLink = links.next();
				tempSrc = nodeMapping.get(vNet.getSource(currVLink));
				tempDst = nodeMapping.get(vNet.getDest(currVLink));
				if (!tempSrc.equals(tempDst)) {
					for (AbstractDemand dem : currVLink) {
						if (dem instanceof BandwidthDemand) {
							tempBwDem = (BandwidthDemand) dem;
							break;
						}
					}
					for (AbstractDemand dem : currVLink.getHiddenHopDemands()) {
						if (dem instanceof CpuDemand) {
							tempHhCpuDem = (CpuDemand) dem;
							break;
						}
					}
					if (!linksToMap.containsKey(Integer.toString(tempSrc
							.getId())
							+ " " + Integer.toString(tempDst.getId()))) {
						out
								.write(" (" + Integer.toString(tempSrc.getId())
										+ ","
										+ Integer.toString(tempDst.getId())
										+ ")");
						linksToMap.put(Integer.toString(tempSrc.getId()) + " "
								+ Integer.toString(tempDst.getId()), tempBwDem
								.getDemandedBandwidth());
						if (tempHhCpuDem != null) {
							linksToMapHhCpu.put(Integer.toString(tempSrc
									.getId())
									+ " " + Integer.toString(tempDst.getId()),
									tempHhCpuDem.getDemandedCycles());
						}
					} else {
						double tempBw = linksToMap.get(Integer.toString(tempSrc
								.getId())
								+ " " + Integer.toString(tempDst.getId()))
								+ tempBwDem.getDemandedBandwidth();
						linksToMap.put(Integer.toString(tempSrc.getId()) + " "
								+ Integer.toString(tempDst.getId()), tempBw);
						if (tempHhCpuDem != null) {
							double temphHCpu = linksToMapHhCpu.get(Integer
									.toString(tempSrc.getId())
									+ " " + Integer.toString(tempDst.getId()))
									+ tempHhCpuDem.getDemandedCycles();
							linksToMapHhCpu.put(Integer.toString(tempSrc
									.getId())
									+ " " + Integer.toString(tempDst.getId()),
									temphHCpu);
						}
					}
				} else {
					// FIXME future work if node overload is considered and a
					// virtual link is mapped from one
					// substrate node to the same substrate node
				}
			}

			// Write substrate links bandwidth capacity
			out.write(";\n\n");
			out.write("param: \t\tLinkCapacity :=\n");
			for (Iterator<SubstrateLink> links = substrate.getEdges()
					.iterator(); links.hasNext();) {
				SubstrateLink temp = links.next();
				for (AbstractResource res : temp) {
					// Change it, and instance of abstract resource
					if (res instanceof BandwidthResource) {
						if (flag) {
							flag = false;
						} else {
							out.write("\n");
						}
						out.write(Integer.toString(substrate.getSource(temp)
								.getId())
								+ " "
								+ Integer.toString(substrate.getDest(temp)
										.getId())
								+ " \t\t"
								+ Double.toString(((BandwidthResource) res)
										.getAvailableBandwidth()));
						break;
					}
				}
			}

			// If there are hidden hops present, write node capacities, (if
			// tempHhCpuDem!=null, hidden hops are considered)
			if (tempHhCpuDem != null || isNodeMapping) {
				out.write(";\n\n");
				out.write("param: \t\tSNodeCapacity :=\n");
				for (Iterator<SubstrateNode> nodes = substrate.getVertices()
						.iterator(); nodes.hasNext();) {
					SubstrateNode temp = nodes.next();
					for (AbstractResource res : temp) {
						// Change it, and instance of abstract resource
						if (res instanceof CpuResource) {
							if (flag) {
								flag = false;
							} else {
								out.write("\n");
							}
							out.write(Integer.toString(temp.getId())
									+ " \t\t\t"
									+ Double.toString(((CpuResource) res)
											.getAvailableCycles()));
							break;
						}
					}
				}
			}

			// Write virtual links bandwidth demands
			out.write(";\n\n");
			out.write("param: \t\tVLinkDemand :=\n");
			for (Iterator<String> linkIterator = linksToMap.keySet().iterator(); linkIterator
					.hasNext();) {
				String currLink = linkIterator.next();
				if (flag) {
					flag = false;
				} else {
					out.write("\n");
				}
				out.write(currLink
						+ " \t\t"
						+ Double.toString(MiscelFunctions
								.roundThreeDecimals(linksToMap.get(currLink))));
			}

			if (isNodeMapping) {
				// Write virtual nodes cpu demand in data file
				out.write(";\n\n");
				out.write("param: \t\tVNodeDemand :=\n");
				mappedNodes = new LinkedList<SubstrateNode>();
				for (Iterator<VirtualNode> itt = vNet.getVertices().iterator(); itt
						.hasNext();) {
					tempVirNode = itt.next();
					tempMapped = nodeMapping.get(tempVirNode);

					if (!mappedNodes.contains(tempMapped)) {
						for (AbstractDemand dem : tempVirNode) {
							if (dem instanceof CpuDemand) {
								if (flag) {
									flag = false;
								} else {
									out.write("\n");
								}
								out.write(Integer.toString(tempMapped.getId())
										+ " \t\t\t"
										+ Double.toString(((CpuDemand) dem)
												.getDemandedCycles()));
								break;
							}
						}

					}
					mappedNodes.add(tempMapped);
				}
			}

			// Write virtual links hidden hops CPU demands
			if (tempHhCpuDem != null && !isNodeMapping) {
				out.write(";\n\n");
				out.write("param: \t\tHHDemand :=\n");
				for (Iterator<String> linkIterator = linksToMapHhCpu.keySet()
						.iterator(); linkIterator.hasNext();) {
					String currLink = linkIterator.next();
					if (flag) {
						flag = false;
					} else {
						out.write("\n");
					}
					out.write(currLink
							+ " \t\t"
							+ Double.toString(MiscelFunctions
									.roundThreeDecimals(linksToMapHhCpu
											.get(currLink))));
				}
			}
			// Write weight of each parameter
			out.write(";\n\n");
			out.write("param w1 := " + Double.toString(wBandwidth) + ";\n");
			out.write("param w2 := " + Double.toString(wCPU) + ";\n");

			if (isNodeMapping)
				out.write("param w3 := " + Double.toString(0.0001) + ";\n");

			out.write("\n\nend;");
			// Close the output stream
			out.close();
		} catch (Exception e) {// Catch exception if any
			System.err.println("Error: " + e.getMessage());
		}

	}

	// PRIVATE //
	private FileWriter fstream;
}